#include "utils/utils.hpp"
#include "HTTPRequest.hpp"
#include "spdlog/spdlog.h"
#include <cstddef>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
http::Response Utils::send_message(const std::string &msg,
                                   const std::string &url) {
  try {
    // you can pass http::InternetProtocol::V6 to Request to make an IPv6
    // request
    spdlog::info("Welcome to spdlog!");
    http::Request request{url};

    const std::string left = "{";
    const std::string right = "}";

    const std::string body = left + "\"data\":" + "\"" + msg + "\"" + right;
    // send a get request
    const auto response =
        request.send("GET", body, {{"Content-Type", "application/json"}});
    return response;
  } catch (const std::exception &e) {
    std::cerr << "Request failed, error: " << e.what() << '\n';
  }
  return http::Response{{{http::Status::InternalServerError}}};
}
void Utils::show_member_value(member_itr_type m, data_type _type) {
  const sonic_json::Node &key = m->name;
  sonic_json::Node &value = m->value;
  spdlog::info("Welcome to spdlog!");

  if (key.IsString()) {
    spdlog::info("key is {}", key.GetString());
  } else {
    spdlog::info("Incorrect key type!");
    return;
  }
  spdlog::info("before switch"); // Prints the type of the value
  switch (_type) {
  case data_string: {
    spdlog::info("String found!");
    if (value.IsString()) {
      spdlog::info("Value is: {}", value.GetString());
    } else if (value.IsNull()) {
      spdlog::info("Value is null");
    } else {
      spdlog::info("Incorrect value type!");
    }
    break;
  case data_int64: {
    spdlog::info("Int64 found!");
    if (value.IsInt64()) {
      spdlog::info("Value is: {}", value.GetInt64());
    } else if (value.IsNull()) {
      spdlog::info("Value is null");
    } else {
      spdlog::info("Incorrect value type!");
    }
    break;
  }
  }
  case data_array:
    spdlog::info("Array found!");

    if (value.IsArray()) {
      for (auto itr = value.Begin(); itr != value.End(); ++itr) {
        if ((*itr).IsObject()) {
          //   std::cout << " is object" << std::endl;
          spdlog::info("is object");
          if ((*itr).FindMember("data")->value.IsString()) {
            std::cout << "-------> "
                      << ((*itr).FindMember("data")->value.GetString())
                      << std::endl;

          } else if ((*itr).FindMember("data")->value.IsObject()) {
            std::cout << "valus is : obj" << std::endl;
          }

        } else if ((*itr).IsInt64()) {
          spdlog::info("is int64");
          std::cout << "------->: " << (*itr).GetInt64() << std::endl;
        } else if ((*itr).IsBool()) {
          std::cout << "------->: " << (*itr).GetBool() << std::endl;
        } else if ((*itr).IsNull()) {
          spdlog::info("Value is null");
        } else {
          spdlog::info("Incorrect value type!");
        }
      }
    }
    break;
  case data_object:
    spdlog::info("Object found!");
    if (value.IsObject()) {
      auto m = value.FindMember("data");
      std::cout << "------->: " << m->value.GetString() << std::endl;
    }
    break;
  }
}
member_itr_type Utils::proc_resp_send_message(http::Response &resp) {
  sonic_json::Document doc;
  auto data = std::string{resp.body.begin(), resp.body.end()};
  doc.Parse(data);
  if (doc.HasParseError()) {
    std::cout << "Parse failed!\n";
    return NULL;
  }
  // Find member by key
  if (!doc.IsObject()) { // Check JSON value type.
    std::cout << "Incorrect doc type!\n";
    return NULL;
  }
  sonic_json::WriteBuffer wb;
  doc.Serialize(wb);
  spdlog::info("{}", wb.ToString());
  auto m = doc.FindMember("data");
  // resp data should be a object
  // exam: {"data": "123","time": "2019-08-30 14:57:26"}
  if (m != doc.MemberEnd()) {
    Utils::show_member_value(m, data_type::data_object);
  } else {
    spdlog::info("Find key doesn't exist!");
  }
  return m;
}

http::Response Utils::get_message(const std::string &url, const int number) {
  try {
    // you can pass http::InternetProtocol::V6 to Request to make an IPv6
    // request
    http::Request request{url};

    const std::string left = "{";
    const std::string right = "}";

    const std::string body =
        left + "\"data\":" + "\"" + std::to_string(number) + "\"" + right;
    // send a get request
    spdlog::info("{}", body);
    const auto response =
        request.send("GET", body, {{"Content-Type", "application/json"}});
    return response;
  } catch (const std::exception &e) {
    std::cerr << "Request failed, error: " << e.what() << '\n';
  }
  return http::Response{{{http::Status::InternalServerError}}};
}
member_itr_type Utils::proc_resp_get_message(http::Response &resp) {
  sonic_json::Document doc;
  auto data = std::string{resp.body.begin(), resp.body.end()};
  doc.Parse(data);
  if (doc.HasParseError()) {
    std::cout << "Parse failed!\n";
    return NULL;
  }
  // Find member by key
  if (!doc.IsObject()) { // Check JSON value type.
    std::cout << "Incorrect doc type!\n";
    return NULL;
  }
  sonic_json::WriteBuffer wb;
  doc.Serialize(wb);
  spdlog::info("{}", wb.ToString());
  auto m = doc.FindMember("data");
  if (m != doc.MemberEnd()) {
  // resp data may be a object or a array
    // 
    if (m->value.IsArray()) {
        Utils::show_member_value(m, data_type::data_array);
    }else if (m->value.IsObject()) {
        Utils::show_member_value(m, data_type::data_object);
    }
  } else {
    spdlog::info("Find key doesn't exist!");
  }
  // exam: {"data": "123","time": "2019-08-30 14:57:26"}
  return m;
}
void Utils::config_write(const std::string &path, const std::string &key,
                         const std::string &value) {
  std::ifstream file(path);
  if (!file.is_open()) {
    spdlog::info("file {} not exist", path);
  }
  std::string data;
  std::string line;
  while (std::getline(file, line)) {
    data += line;
  }
  file.close();
  sonic_json::Document doc;
  doc.Parse(data);
  if (doc.HasParseError()) {
    spdlog::info("Parse failed!\n");
  }
  // Find member by key
  if (!doc.IsObject()) { // Check JSON value type.
    spdlog::info("Incorrect doc type!\n");
  }
  auto m = doc.FindMember(key);
  if (m != doc.MemberEnd()) {
    sonic_json::Node& node_value = m->value;
    node_value.SetString(value);
  }
  sonic_json::WriteBuffer wb;
  doc.Serialize(wb);
  std::cout << wb.ToString() << std::endl;
  std::ofstream outfile(path);
  if (!outfile.is_open()) {
    spdlog::info("file {} not exist", path);
  }
  outfile << wb.ToString();
  outfile.close();
}
void Utils::update_config(const std::string& path, const std::string &key,const std::string &value){
  if (strcmp(key.c_str(), "base_url")==0 || strcmp(key.c_str(), "send_url")==0 || strcmp(key.c_str(), "get_url")==0) {
    spdlog::info("key is {}", key);
    config_write(path, key, value);
  }
}
void Utils::usage(){
  std::cout << "Usage: ./fiu <command> <args> \n";
  std::cout << "command: \n";
  std::cout << "  -h -help: help \n";
  std::cout << "  -u -update: update config, key-value pairs\n";
  std::cout << "    ./fiu -u <key> <value>\n";
  std::cout << "  -s -send: send message\n";
  std::cout << "    ./fiu -s <msg>\n";
  std::cout << "  -g -get: get messages, \n";
  std::cout << "    ./fiu -g <number>\n";
  std::cout << "  examples:\n";
  std::cout << "    ./fiu -u base_url http://127.0.0.1:8080/\n";
  std::cout << "    ./fiu -u send_url send_message\n";
  std::cout << "    ./fiu -u get_url get_message\n";
  std::cout << "    ./fiu -s 123\n";
  std::cout << "    ./fiu -g 1\n";
}